// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Matrix.ServiceCommunication.Options;
using Matrix.ServiceCommunication.Services;
using Matrix.ServiceCommunication.Discovery.LoadBalancing;

namespace Matrix.ServiceCommunication.Discovery;

/// <summary>
/// 内存服务注册中心
/// </summary>
public class InMemoryServiceRegistry : IServiceDiscoveryProvider, IHostedService, IDisposable
{
    private readonly IOptionsMonitor<ServiceCommunicationOptions> _options;
    private readonly ILogger<InMemoryServiceRegistry> _logger;
    private readonly IMemoryCache _cache;
    private readonly HttpClient _httpClient;
    private readonly ILoadBalancer _loadBalancer;
    private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ServiceInstance>> _services;
    private readonly Timer _cleanupTimer;
    private readonly SemaphoreSlim _semaphore;
    private readonly TimeSpan _cleanupInterval;
    private readonly TimeSpan _serviceTtl;
    private bool _disposed;

    public InMemoryServiceRegistry(
        IOptionsMonitor<ServiceCommunicationOptions> options,
        ILogger<InMemoryServiceRegistry> logger,
        IMemoryCache cache,
        IHttpClientFactory httpClientFactory,
        ILoadBalancer loadBalancer)
    {
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _httpClient = httpClientFactory.CreateClient("HealthCheck");
        _loadBalancer = loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer));
        _services = new ConcurrentDictionary<string, ConcurrentDictionary<string, ServiceInstance>>();
        _semaphore = new SemaphoreSlim(1, 1);

        var discoveryOptions = options.CurrentValue.EnhancedDiscovery;
        _cleanupInterval = TimeSpan.FromSeconds(discoveryOptions.HealthCheckIntervalSeconds);
        _serviceTtl = TimeSpan.FromSeconds(discoveryOptions.ServiceRegistrationTtlSeconds);

        // 启动清理定时器
        _cleanupTimer = new Timer(CleanupExpiredServices, null, _cleanupInterval, _cleanupInterval);

        // 订阅配置变更
        _options.OnChange(OnConfigurationChanged);

        // 初始化预配置的服务
        InitializePreconfiguredServices();

        _logger.LogInformation("内存服务注册中心已初始化，清理间隔: {CleanupInterval}s, 服务TTL: {ServiceTtl}s",
            _cleanupInterval.TotalSeconds, _serviceTtl.TotalSeconds);
    }

    public Task<Uri> GetServiceAddressAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var instances = GetServiceInstancesAsync(serviceName, cancellationToken);
        var healthyInstances = instances.Result.Where(i => i.Healthy && i.Enabled).ToList();

        if (!healthyInstances.Any())
        {
            _logger.LogWarning("服务 '{ServiceName}' 没有健康的实例", serviceName);
            throw new InvalidOperationException($"服务 '{serviceName}' 没有健康的实例");
        }

        var selectedInstance = _loadBalancer.Select(healthyInstances);
        var address = selectedInstance.Address;

        _logger.LogDebug("为服务 {ServiceName} 选择地址: {Address} (负载均衡策略: {Strategy})",
            serviceName, address, _loadBalancer.StrategyName);

        return Task.FromResult(address);
    }

    public Task<IReadOnlyList<Uri>> GetServiceAddressesAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var instances = GetServiceInstancesAsync(serviceName, cancellationToken);
        var healthyInstances = instances.Result
            .Where(i => i.Healthy && i.Enabled)
            .Select(i => i.Address)
            .ToList()
            .AsReadOnly();

        return Task.FromResult<IReadOnlyList<Uri>>(healthyInstances);
    }

    public Task RegisterServiceAsync(ServiceRegistration registration, CancellationToken cancellationToken = default)
    {
        if (registration == null)
        {
            throw new ArgumentNullException(nameof(registration));
        }

        if (string.IsNullOrWhiteSpace(registration.ServiceName))
        {
            throw new ArgumentException("服务名称不能为空", nameof(registration));
        }

        if (string.IsNullOrWhiteSpace(registration.ServiceId))
        {
            throw new ArgumentException("服务ID不能为空", nameof(registration));
        }

        var instance = new ServiceInstance
        {
            ServiceId = registration.ServiceId,
            ServiceName = registration.ServiceName,
            Address = registration.Address,
            Port = registration.Port,
            Weight = registration.Weight,
            Enabled = registration.Enabled,
            Healthy = true, // 新注册的服务默认健康
            RegisteredAt = DateTime.UtcNow,
            LastHeartbeat = DateTime.UtcNow,
            HealthCheckUrl = registration.HealthCheckUrl,
            Tags = registration.Tags.ToList(),
            Metadata = registration.Metadata.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
        };

        var serviceInstances = _services.GetOrAdd(registration.ServiceName, _ => new ConcurrentDictionary<string, ServiceInstance>());
        serviceInstances.AddOrUpdate(registration.ServiceId, instance, (_, _) => instance);

        _logger.LogInformation("服务注册成功: {ServiceId} ({ServiceName}) -> {Address}",
            registration.ServiceId, registration.ServiceName, registration.Address);

        return Task.CompletedTask;
    }

    public Task UnregisterServiceAsync(string serviceName, string serviceId, CancellationToken cancellationToken = default)
    {
        if (_services.TryGetValue(serviceName, out var serviceInstances))
        {
            if (serviceInstances.TryRemove(serviceId, out var removedInstance))
            {
                _logger.LogInformation("服务注销成功: {ServiceId} ({ServiceName})",
                    serviceId, serviceName);

                // 如果服务下没有实例了，删除服务
                if (serviceInstances.IsEmpty)
                {
                    _services.TryRemove(serviceName, out _);
                    _logger.LogDebug("服务 {ServiceName} 已无实例，已删除", serviceName);
                }
            }
            else
            {
                _logger.LogWarning("服务注销失败，未找到实例: {ServiceId} ({ServiceName})",
                    serviceId, serviceName);
            }
        }
        else
        {
            _logger.LogWarning("服务注销失败，未找到服务: {ServiceName}", serviceName);
        }

        return Task.CompletedTask;
    }

    public Task<IReadOnlyList<ServiceInfo>> GetServicesAsync(CancellationToken cancellationToken = default)
    {
        var services = _services.Select(kvp => new ServiceInfo
        {
            ServiceName = kvp.Key,
            Instances = kvp.Value.Values.ToList()
        }).ToList().AsReadOnly();

        return Task.FromResult<IReadOnlyList<ServiceInfo>>(services);
    }

    private async Task<List<ServiceInstance>> GetServiceInstancesAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        // 检查缓存
        var cacheKey = $"service_instances:{serviceName}";
        if (_cache.TryGetValue(cacheKey, out List<ServiceInstance>? cachedInstances) && cachedInstances != null)
        {
            return cachedInstances;
        }

        // 从注册中心获取
        if (!_services.TryGetValue(serviceName, out var serviceInstances))
        {
            return new List<ServiceInstance>();
        }

        var instances = serviceInstances.Values.ToList();

        // 检查健康状态
        await CheckHealthAsync(instances, cancellationToken);

        // 缓存结果
        var cacheOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30)
        };
        _cache.Set(cacheKey, instances, cacheOptions);

        return instances;
    }

    private async Task CheckHealthAsync(List<ServiceInstance> instances, CancellationToken cancellationToken = default)
    {
        var tasks = instances.Select(CheckInstanceHealthAsync);
        await Task.WhenAll(tasks);
    }

    private async Task CheckInstanceHealthAsync(ServiceInstance instance)
    {
        if (string.IsNullOrEmpty(instance.HealthCheckUrl))
        {
            // 如果没有健康检查URL，认为健康
            instance.Healthy = true;
            instance.LastHeartbeat = DateTime.UtcNow;
            return;
        }

        try
        {
            var request = new HttpRequestMessage(HttpMethod.Get, instance.HealthCheckUrl);
            request.Headers.Add("User-Agent", "Matrix.ServiceCommunication/HealthChecker/1.0");

            using var response = await _httpClient.SendAsync(request);
            var isHealthy = response.IsSuccessStatusCode;

            instance.Healthy = isHealthy;
            instance.LastHeartbeat = DateTime.UtcNow;

            if (isHealthy)
            {
                _logger.LogTrace("健康检查通过: {ServiceId} -> {HealthCheckUrl}",
                    instance.ServiceId, instance.HealthCheckUrl);
            }
            else
            {
                _logger.LogWarning("健康检查失败: {ServiceId} -> {HealthCheckUrl}, 状态码: {StatusCode}",
                    instance.ServiceId, instance.HealthCheckUrl, response.StatusCode);
            }
        }
        catch (Exception ex)
        {
            instance.Healthy = false;
            instance.LastHeartbeat = DateTime.UtcNow;

            _logger.LogWarning(ex, "健康检查异常: {ServiceId} -> {HealthCheckUrl}",
                instance.ServiceId, instance.HealthCheckUrl);
        }
    }

    private void CleanupExpiredServices(object? state)
    {
        if (_disposed) return;

        try
        {
            _logger.LogDebug("开始清理过期服务实例");
            var now = DateTime.UtcNow;
            var expiredCount = 0;

            foreach (var serviceKvp in _services)
            {
                var serviceName = serviceKvp.Key;
                var serviceInstances = serviceKvp.Value;

                var expiredInstances = serviceInstances.Values
                    .Where(instance => now - instance.LastHeartbeat > _serviceTtl)
                    .ToList();

                if (expiredInstances.Any())
                {
                    foreach (var expiredInstance in expiredInstances)
                    {
                        if (serviceInstances.TryRemove(expiredInstance.ServiceId, out _))
                        {
                            expiredCount++;
                            _logger.LogDebug("清理过期实例: {ServiceId} ({ServiceName}), 最后心跳: {LastHeartbeat}",
                                expiredInstance.ServiceId, serviceName, expiredInstance.LastHeartbeat);
                        }
                    }

                    // 如果服务下没有实例了，删除服务
                    if (serviceInstances.IsEmpty)
                    {
                        _services.TryRemove(serviceName, out _);
                    }
                }
            }

            if (expiredCount > 0)
            {
                _logger.LogInformation("清理过期服务实例完成，共清理 {Count} 个实例", expiredCount);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "清理过期服务实例时发生异常");
        }
    }

    private void OnConfigurationChanged(ServiceCommunicationOptions newOptions)
    {
        // 配置变更时清理缓存
        _cache.Compact(1.0);
        _logger.LogInformation("配置已变更，已清理服务实例缓存");
    }

    private void InitializePreconfiguredServices()
    {
        var preconfiguredServices = new[]
        {
            new ServiceRegistration
            {
                ServiceName = "AuthService",
                ServiceId = "auth-service-1",
                Address = new Uri("http://localhost:5001"),
                Port = 5001,
                HealthCheckUrl = "http://localhost:5001/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "auth", "security" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "UserService",
                ServiceId = "user-service-1",
                Address = new Uri("http://localhost:5002"),
                Port = 5002,
                HealthCheckUrl = "http://localhost:5002/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "user", "identity" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "TenantService",
                ServiceId = "tenant-service-1",
                Address = new Uri("http://localhost:5003"),
                Port = 5003,
                HealthCheckUrl = "http://localhost:5003/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "tenant", "management" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "ConfigService",
                ServiceId = "config-service-1",
                Address = new Uri("http://localhost:5004"),
                Port = 5004,
                HealthCheckUrl = "http://localhost:5004/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "config", "management" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "NotificationService",
                ServiceId = "notification-service-1",
                Address = new Uri("http://localhost:5005"),
                Port = 5005,
                HealthCheckUrl = "http://localhost:5005/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "notification", "messaging" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "FileStorageService",
                ServiceId = "file-storage-service-1",
                Address = new Uri("http://localhost:5006"),
                Port = 5006,
                HealthCheckUrl = "http://localhost:5006/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "storage", "file" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "1.0.0",
                    ["environment"] = "development"
                }
            },
            new ServiceRegistration
            {
                ServiceName = "KeycloakService",
                ServiceId = "keycloak-service-1",
                Address = new Uri("http://localhost:8080"),
                Port = 8080,
                HealthCheckUrl = "http://localhost:8080/health",
                Weight = 1,
                Enabled = true,
                Tags = new List<string> { "auth", "keycloak" },
                Metadata = new Dictionary<string, string>
                {
                    ["version"] = "26.0",
                    ["environment"] = "development"
                }
            }
        };

        foreach (var service in preconfiguredServices)
        {
            try
            {
                RegisterServiceAsync(service).Wait();
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "初始化预配置服务失败: {ServiceName}", service.ServiceName);
            }
        }

        _logger.LogInformation("预配置服务初始化完成，共 {Count} 个服务", preconfiguredServices.Length);
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("内存服务注册中心已启动");
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("内存服务注册中心已停止");
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        if (_disposed) return;

        _disposed = true;
        _cleanupTimer?.Dispose();
        _semaphore?.Dispose();
        _httpClient?.Dispose();

        _logger.LogDebug("内存服务注册中心已释放");
    }
}